//----------------------------------------------------------------------------------------------
//	Filename:	PointCloudSegmentation.h
//	Author:		Sandro Spina
//	Date:		12/02/2013
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
#pragma once

#include <Scene\Environment.h>
#include <Shape\KDTreePointCloud.h>
#include <Scene\GeometricPrimitive.h>
#include "FeatureDescStructureGraph.h"
#include "SegmentsGraph.h"
#include "StructureGraph.h"

namespace Illumina
{
	namespace Core
	{
		class PointCloudSegmentation
		{
			public:
				
			struct PCS_Descriptor {	
				std::vector<ObjStructureGraph *> coverage_principal_structures;
				std::vector<ObjStructureGraph *> visibility_principal_structures;

				int sizeInBytes()
				{
					int s = 0;					
					return s;
				}
			};				

				
			int m_id;									//Unique identifier for PCS instances ... one per model/scene and is set (and verified) by user. Ids starting from 10000 are used for training models, 20000 used for scenes.
			Environment *m_pEnvironment;
			EngineKernel *m_pEngineKernel;
			KDTreePointCloud *m_pointCloud;
				
			StructureGraph *m_structureGraph;
			SegmentsGraph *m_segmentsGraph;
				
			int m_maxSurfacesVisibleFromViews;							//Stores the theoretical maximum number of surfaces that can be seen from one view point (from training data)
			int m_mostVisibileSurface;									//Stores the ID of the surface segment which is most visible from all view points.
			std::map<int, int> m_surfaceVisiblityCount;
			float m_ransacPlaneFitEpsilon;

			SegmentsGraph::OBB m_obb;

			std::vector<FeatureDescStructureGraph*> * m_featureDescriptionGraphs;	//Stores the important segments + feature info of this PCS
			std::map<std::pair<int,int>, int> * m_edgeOcclusions;					//A map storing occlusion information between surface segment pairs. std::pair<src,sink>,viewcount

			std::vector<StructureGraph *> * m_twoStateFeatures;						//A list of the most prominent two state features (as structure graphs). Used to perform searches.
			std::vector<StructureGraph *> * m_threeStateFeatures;					//A list of the most priminent three state features (as structure graphs). Used to perform searches.

			/*
			 *	Assign scene environment, enginekernel and create matte materials for use with the segments.
			 */
			PointCloudSegmentation(Environment* p_environment, std::string _path, int p_id, bool &p_loaded);															//Constructor used when LOADING a pcs file
			PointCloudSegmentation(Environment* p_environment, GeometricPrimitive * p_geometricPrimitive, int p_id, float m_ransacEpsilon);								//Constructor for PCS structure of main scene 
			PointCloudSegmentation(Environment* p_environment, GeometricPrimitive * p_geometricPrimitive, KDTreePointCloud * p_cloud, int p_id, float m_ransacEpsilon);	//Constructor for PCS structure of models used for training
			
			//SceneSolution * GetSceneSolution() { return m_sceneSolution; }
			StructureGraph * GetStructureGraph() { return m_structureGraph; }
			SegmentsGraph * GetSegmentsGraph() { return m_segmentsGraph; }

			//**************************************************
			//**********Graph Creation Functions****************
			//Create a structure graph from the segments graph.
			StructureGraph * ComputeSceneStructureGraphFromSceneSegmentsGraph(SegmentsGraph * segmentsGraph, int p_wt);
		
			//Generate graph in two steps ... first produce segments then connect them into a segments graph
			SegmentsGraph * GenerateSceneSegmentsGraph(int p_k, int p_wt);

			void ComputeOverlapBetweenConnectedSegmentsOBB();
			std::pair<Vector3, std::pair<float,float>> ComputeRatioOfPointsAboveAndUnder(int p_segmentId, int p_intSegmentId);

			//Determines the points coverage for each segment
			void ComputePointCoveragePerSegment(int _sampleSize);

			void ComputeVoxelGridAroundDownsampledSegment(std::vector<int> p_segmentIds, FeatureDescStructureGraph::VoxelGrid * p_vg, int p_gridLod, float _rotationVWPlane, 
																					Vector3 &min_uvw_translate, Vector3 &max_uvw_translate, FeatureDescStructureGraph::VoxelGrid * _vgrid, std::map<int, std::vector<int>*> * p_downsampledSegmnets);

			void AugmentVoxelGridWithEdgeSegments(FeatureDescStructureGraph::VoxelGrid * voxel_grid, std::vector<int> neighbourEdgeSegments);

			FeatureDescStructureGraph::VoxelGrid * ComputeVoxelGridAroundSegment(std::vector<int> p_segmentIds, FeatureDescStructureGraph::VoxelGrid * p_vg, int p_gridLod, float _rotationVWPlane,
																					Vector3 &min_uvw_translate, Vector3 &max_uvw_translate);	

			void ComputeVoxelGridAroundSegment(std::vector<int> p_segmentIds, FeatureDescStructureGraph::VoxelGrid * p_vg, int p_gridLod, float _rotationVWPlane,
																					Vector3 &min_uvw_translate, Vector3 &max_uvw_translate, FeatureDescStructureGraph::VoxelGrid * _vgrid);	

			//Compute Feature Structure Graphs Matrix
			bool ComputeFeatureMatrix(int _featureGraphsCount, int _graphDepth, float p_cell_size);
			FeatureDescStructureGraph * GetFDG(int _id);

			//Compare Feature Matrix is called on the models trained (in database).
			std::pair<float, std::vector<int>> ScoreFeatureMatrix(PointCloudSegmentation * _otherObject, int _segmentId); //_otherObject may well be the scene itself, _segmentId is the segment we want to compare against. 
						
			//Compute Feature Structure Graphs Matrix
			void ComputeOBB(float _buffer);

			//**********************************************************************
			//**********Graph Learning and Generalisation Functions****************	
			//After the FULL segments, edge and structure graphs have been built use individual views on the object to add info (e.g. hidden edges) to the graphs
			void EnhanceGraphsFromSingleView(PointCloudSegmentation* p_viewPCS, int p_viewIdx);

			//Function to change point type to those points which have been removed since their segments where pruned from the graph.
			void MarkPointsAsPruned(std::vector<int> point_indices);

			//Merge a list of states in the structure graph together according to some heuristic
			//Optionally starting from surface _segmentId
			//_connectivity : 1=direct, 2=transitive; _userSet : 1=user given segment id, 2=automatic; _level : 1=internal states, 0=graph states.
			void MergeStates(bool _userSet, int _segmentId, int _connectivity, float _angle, float _epsilon, int _level);

			void MergeInternalStates(bool _userSet, SegmentsGraph::SurfaceSegment * _segment, int _connectivity, float _angle, float _epsilon);

			//Given the segments, structure and edge graphs, use state merging techniques to learn a more generic structure. Populates the features vectors
			void GeneraliseGraphs();

			//Utility method which checks if a segment is visible given a partial view.
			bool IsSegmentVisible(int p_segmentId, std::map<std::pair<int,int>, int> &p_map);

			//Prints varies statistics realted to this PCS model. //e.g. number of states, most visible surface, etc.
			void PrintStatistics();
			void PrintOptimalFeatureStrings();

			//**********************************************************************
			//********************RANSAC Model Fitting Functions********************			
			int RANSACManager(SegmentsGraph::SurfaceSegment * p_segment, float p_errorTolerance, int p_stableCount, float p_fitRatio, bool p_edgeApplication);
			//All these functions return a surface segment
			bool RANSACFitPlane(float p_errorTolerance, float p_fitRatio, int p_stableCount, SegmentsGraph::SurfaceSegment * p_segment, std::vector<int> &p_indices, int &p_sp1, int &p_sp2, int &p_sp3, Vector3 &p_sn);
			bool RANSACFitSphere(float p_errorTolerance, float p_fitRatio, int p_stableCount, SegmentsGraph::SurfaceSegment * p_segment, std::vector<int> &p_indices);
			bool RANSACFitCylinder(float p_errorTolerance, float p_fitRatio, int p_stableCount, SegmentsGraph::SurfaceSegment * p_segment, std::vector<int> &p_indices);

			//**********************************************
			//**********Serialisation Functions********************
			void WriteToBPCSFile(std::string _filepath);
			bool ReadFromBPCSFile(std::string _filepath); 
			//void FitStructuresToScene_v2(SegmentsGraph * &pSeG, StructureGraph * &pStG);	

			//**********************************************
			//**********Logs Writing Functions**************
			//void WriteCutsToFile(std::map<int, std::vector<GraphCut*>*> * &cuts, const char * filename);
	
			//**********************************************
			//***********Graph Drawing Functions************
			void DrawSceneGraph(StructureGraph * segmentGraph, std::string p_filepath, float p_obb_overlap);	
			void DrawSegmentsGraph(SegmentsGraph * segmentGraph, std::string p_filepath);

			//Perhaps this should be in the KDTreePointCloud class.
			//***************************************************
			//***********PointCloud Drawing Functions************	
			void WritePointsToBinaryFile(KDTreePointCloud &p_pointCloud, std::string _filepath);

			std::string ToString();
		};
	}
}